package com.asset.dispoal.business.mediation.service;

import com.asset.dispoal.business.bsCase.domain.db.entity.BsCase;
import com.asset.dispoal.business.casedispose.domain.db.mapper.CasedisposeMapper;
import com.asset.dispoal.business.casedispose.domain.dto.CaseDisposeFormulaDTO;
import com.asset.dispoal.business.casedispose.domain.dto.CaseDisposeFormulaDetailDTO;
import com.asset.dispoal.business.mediation.domain.db.entity.AgreementCaseInfo;
import com.asset.dispoal.business.mediation.domain.db.mapper.ReduceOperateMapper;
import com.asset.dispoal.business.mediation.dto.CalculateRateRequest;
import com.asset.dispoal.business.mediation.dto.CalculateRateResponse;
import com.asset.dispoal.business.mediation.dto.JudgmentDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tech.mhuang.pacebox.core.date.DateTimeUtil;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
 * @description: 策略模式 map + function实现
 * @author: yf
 * @date: 2022-08-30 10:26:24
 */
@Slf4j
@Service
public class CalculateService {
    public static Map<String, Function<CalculateRateRequest, CalculateRateResponse>> calculateMethodMap = new HashMap<>();
    @Autowired
    private ReduceOperateMapper operateMapper;
    @Autowired
    CasedisposeMapper casedisposeMapper;


    @PostConstruct
    void init() {
        //逾期利率
        calculateMethodMap.put("0", this::calPenaltyInterest);
        //利息利率
        calculateMethodMap.put("1", this::calInterestRate);
    }

    public CalculateRateResponse getResult(CalculateRateRequest request) {
        Function<CalculateRateRequest, CalculateRateResponse> function = calculateMethodMap.get(request.getTypeOfDeduction());
        if (function != null) {
            return function.apply(request);
        }
        log.error("暂时未提供其他算法或者未找到其他算法!!!");
        return null;
    }


    /**
     * 利息利率的算法  利息 当前利息1 * 减免值 / 利息利率
     *
     * @param request
     * @return
     */
    private CalculateRateResponse calInterestRate(CalculateRateRequest request) {
        //根据caseId获取合同日减免利率
        List<AgreementCaseInfo> agreementInfos = operateMapper.queryAgreementRateByCaseId(request.getCaseId());

        //原利息总和
        BigDecimal interest = agreementInfos.stream().map(AgreementCaseInfo::getLawsuitInterestAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        //减免后的利息总和
        BigDecimal interestAmount = new BigDecimal(request.getLawsuitInterestAmount());
        //两个值相等，未发生减免，直接计算
        BigDecimal result = BigDecimal.ZERO;
        if (interest.compareTo(interestAmount) == 0) {
            for (AgreementCaseInfo agreementInfo : agreementInfos) {
                result = result.add(
                        agreementInfo.getLawsuitInterestAmount().
                                multiply(new BigDecimal(request.getScaleOfDeduction())).
                                divide(agreementInfo.getDailyInterestRate(), 2, RoundingMode.DOWN)
                );
            }
        } else {
            //前端扣除的利息金额, 从合同中依次扣减
            BigDecimal interestAmt = interest.subtract(new BigDecimal(request.getLawsuitInterestAmount()));
            for (AgreementCaseInfo agreementInfo : agreementInfos) {
                if (interestAmt.compareTo(agreementInfo.getLawsuitInterestAmount()) > -1) {
                    interestAmt = interestAmt.subtract(agreementInfo.getLawsuitInterestAmount());
                } else {
                    result = result.add(
                            (agreementInfo.getLawsuitInterestAmount().subtract(interestAmt)).
                                    multiply(new BigDecimal(request.getScaleOfDeduction())).
                                    divide(agreementInfo.getDailyInterestRate(), 2, RoundingMode.DOWN)
                    );
                    interestAmt = BigDecimal.ZERO;
                }
            }
        }
        CalculateRateResponse rateResponse = new CalculateRateResponse();
        rateResponse.setLawsuitInterestAmount(result.setScale(2).toPlainString());
        rateResponse.setLawsuitPrincipalAmount(request.getLawsuitPrincipalAmount());
        rateResponse.setLitigationFines(request.getLitigationFines());
        //总额计算
        String total = result.add(new BigDecimal(request.getLawsuitPrincipalAmount())).
                add(new BigDecimal(request.getLitigationFines())).setScale(2).toPlainString();
        rateResponse.setTotalAmt(total);
        return rateResponse;
    }

    /**
     * 逾期利率减免计算
     *
     * @param request
     * @return
     */
    private CalculateRateResponse calPenaltyInterest(CalculateRateRequest request) {
        List<JudgmentDto> judgments = getJudgments(request.getCaseId());

        BigDecimal penaltyInterestAmount = BigDecimal.ZERO;
        for (JudgmentDto judgmentDto : judgments) {
            if ("固定".equals(judgmentDto.getJudgmentType())) {
                //减免后的利率
                BigDecimal interestRateAfter = new BigDecimal(judgmentDto.getJudgmentRate()).subtract(new BigDecimal(request.getScaleOfDeduction()));
                if (interestRateAfter.compareTo(BigDecimal.ZERO) < 1) {
                    continue;
                }
                BigDecimal calculate = calculate(judgmentDto.getJudgmentCalculationTime(), null, judgmentDto.getBaseNumber(), interestRateAfter.toString(), judgmentDto.getJudgmentDay());
                penaltyInterestAmount = penaltyInterestAmount.add(calculate == null ? BigDecimal.ZERO : calculate);
            } else {
                CaseDisposeFormulaDTO caseDisposeFormulaDTO = casedisposeMapper.getFormulaDataById(judgmentDto.getJudgmentRate());
                for (CaseDisposeFormulaDetailDTO item : caseDisposeFormulaDTO.getDetailList()) {
                    BigDecimal interestRateAfter = item.getBigRate().subtract(new BigDecimal(request.getScaleOfDeduction()));
                    if (interestRateAfter.compareTo(BigDecimal.ZERO) < 1) {
                        continue;
                    }
                    //减免后的利率
                    item.setBigRate(interestRateAfter);
                    BigDecimal calculate = calculate(judgmentDto.getJudgmentCalculationTime(), item, judgmentDto.getBaseNumber(), null, null);
                    penaltyInterestAmount = penaltyInterestAmount.add(calculate == null ? BigDecimal.ZERO : calculate);
                }
            }
        }
        CalculateRateResponse rateResponse = new CalculateRateResponse();
        rateResponse.setLawsuitInterestAmount(request.getLawsuitInterestAmount());
        rateResponse.setLawsuitPrincipalAmount(request.getLawsuitPrincipalAmount());
        rateResponse.setLitigationFines(penaltyInterestAmount.toString());
        //总额计算
        String total = penaltyInterestAmount.add(new BigDecimal(request.getLawsuitPrincipalAmount())).
                add(new BigDecimal(request.getLawsuitInterestAmount())).setScale(2).toPlainString();
        rateResponse.setTotalAmt(total);
        return rateResponse;
    }

    private BigDecimal calculate(Date judgmentCalculationTime, CaseDisposeFormulaDetailDTO item, BigDecimal baseNumber, String judgmentRate, Integer judgmentDay) {
        if (item != null) {
            //如果计算的时间大于开始时间则进行处理，如果不大于开始时间则不允许进行运算
            if (judgmentCalculationTime.after(item.getStartTime())) {
                //如果计算时间大于结束时间
                if (judgmentCalculationTime.after(item.getEndTime())) {
                    judgmentCalculationTime = item.getEndTime();
                }
                //取得范围天数
                long diffDay = DateTimeUtil.getLocalDateDiffDay(DateTimeUtil.dateToLocalDate(item.getStartTime()), DateTimeUtil.dateToLocalDate(judgmentCalculationTime));
                return baseNumber.multiply(BigDecimal.valueOf(diffDay)).multiply(item.getBigRate()).setScale(2, RoundingMode.HALF_UP);
            }
            return BigDecimal.ZERO;
        } else {
            long diffDay = DateTimeUtil.getLocalDateDiffDay(DateTimeUtil.dateToLocalDate(judgmentCalculationTime), LocalDate.now());
            return baseNumber.multiply(BigDecimal.valueOf(diffDay)).multiply(new BigDecimal(judgmentRate)).divide(judgmentDay == null ? BigDecimal.ONE : BigDecimal.valueOf(judgmentDay), 2, RoundingMode.HALF_UP);
        }
    }

    private List<JudgmentDto> getJudgments(String caseId) {
        BsCase bsCase = operateMapper.queryBsCaseById(caseId);
        ArrayList<JudgmentDto> judgmentDtos = new ArrayList<>(5);
        if (null != bsCase.getJudgmentType1()) {
            JudgmentDto judgmentDto1 = new JudgmentDto();
            judgmentDto1.setBaseNumber(bsCase.getBaseNumber1());
            judgmentDto1.setJudgmentType(bsCase.getJudgmentType1());
            judgmentDto1.setJudgmentRate(bsCase.getJudgmentRate1());
            judgmentDto1.setJudgmentCalculationTime(bsCase.getJudgmentCalculationTime1());
            judgmentDto1.setJudgmentDay(bsCase.getJudgmentDay1());
            judgmentDtos.add(judgmentDto1);
        }
        if (null != bsCase.getJudgmentType2()) {
            JudgmentDto judgmentDto2 = new JudgmentDto();
            judgmentDto2.setBaseNumber(bsCase.getBaseNumber2());
            judgmentDto2.setJudgmentType(bsCase.getJudgmentType2());
            judgmentDto2.setJudgmentRate(bsCase.getJudgmentRate2());
            judgmentDto2.setJudgmentCalculationTime(bsCase.getJudgmentCalculationTime2());
            judgmentDto2.setJudgmentDay(bsCase.getJudgmentDay2());
            judgmentDtos.add(judgmentDto2);
        }
        if (null != bsCase.getJudgmentType3()) {
            JudgmentDto judgmentDto3 = new JudgmentDto();
            judgmentDto3.setBaseNumber(bsCase.getBaseNumber3());
            judgmentDto3.setJudgmentType(bsCase.getJudgmentType3());
            judgmentDto3.setJudgmentRate(bsCase.getJudgmentRate3());
            judgmentDto3.setJudgmentCalculationTime(bsCase.getJudgmentCalculationTime3());
            judgmentDto3.setJudgmentDay(bsCase.getJudgmentDay3());
            judgmentDtos.add(judgmentDto3);
        }
        if (null != bsCase.getJudgmentType4()) {
            JudgmentDto judgmentDto4 = new JudgmentDto();
            judgmentDto4.setBaseNumber(bsCase.getBaseNumber4());
            judgmentDto4.setJudgmentType(bsCase.getJudgmentType4());
            judgmentDto4.setJudgmentRate(bsCase.getJudgmentRate4());
            judgmentDto4.setJudgmentCalculationTime(bsCase.getJudgmentCalculationTime4());
            judgmentDto4.setJudgmentDay(bsCase.getJudgmentDay1());
            judgmentDtos.add(judgmentDto4);
        }
        if (null != bsCase.getJudgmentType5()) {
            JudgmentDto judgmentDto5 = new JudgmentDto();
            judgmentDto5.setBaseNumber(bsCase.getBaseNumber5());
            judgmentDto5.setJudgmentType(bsCase.getJudgmentType5());
            judgmentDto5.setJudgmentRate(bsCase.getJudgmentRate5());
            judgmentDto5.setJudgmentCalculationTime(bsCase.getJudgmentCalculationTime5());
            judgmentDto5.setJudgmentDay(bsCase.getJudgmentDay5());
            judgmentDtos.add(judgmentDto5);
        }
        return judgmentDtos;
    }
}
